//MIT License
//Copyright (c) 2021 cloudguan rcloudguan@163.com
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:

//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.

package net

import (
	"gitee.com/dennis-kk/service-box-go/common"
	"gitee.com/dennis-kk/service-box-go/util/slog"
	"github.com/panjf2000/gnet"
	"sync"
	"sync/atomic"
	"time"
)

type srvMaps map[string]gnet.Server

//serverEH gnet Server loop
type (
	gnetEventHanlderBase struct {
		*gnet.EventServer                     // gnet interface, user implement
		connMap           map[string]IBoxConn //box's connections map
		status            int32               // loop status
		loop              IBoxNetLoop         // owner pointer
		rw                sync.RWMutex        // read write lock
	}

	serverEH struct {
		*gnetEventHanlderBase            //base event handle function
		servers               srvMaps    // gnet framework basic struct
		mu                    sync.Mutex // milt goroutines mutex
	}

	clientEH struct {
		*gnetEventHanlderBase
	}
)

func makeServerEH(loop IBoxNetLoop) *serverEH {
	return &serverEH{
		gnetEventHanlderBase: &gnetEventHanlderBase{
			status:  0,
			loop:    loop,
			connMap: make(map[string]IBoxConn),
			rw:      sync.RWMutex{},
		},
		servers: make(srvMaps),
		mu:      sync.Mutex{},
	}

}

func makeClientEH(loop IBoxNetLoop) *clientEH {
	return &clientEH{
		gnetEventHanlderBase: &gnetEventHanlderBase{
			status: 0,
			loop:   loop,
		},
	}
}

func checkLoopValid(b *gnetEventHanlderBase) (out []byte, action gnet.Action) {
	if b == nil {
		//TODO rpc build error code
		action = gnet.Close
		return
	}

	if atomic.LoadInt32(&b.status) != common.NetworkRunning {
		//TODO rpc build error code
		action = gnet.Close
		return
	}

	if b.loop == nil {
		//TODO rpc build error code
		action = gnet.Close
		return
	}

	return
}

func (b *gnetEventHanlderBase) IsRunning() bool {
	if b == nil {
		return false
	}
	return atomic.LoadInt32(&b.status) == common.NetworkRunning
}

//React a socket receives Data from the peer.
//message workflow gnet->decode->handler->network->transport
func (b *gnetEventHanlderBase) React(frame []byte, c gnet.Conn) (out []byte, action gnet.Action) {
	out, action = checkLoopValid(b)
	if action != gnet.None {
		slog.Error("[Net] 0,%s,0 Service Box Network moudle error !", c.RemoteAddr().String())
		return
	}
	//construct receive event
	rEvent := &ReceiveEvent{
		TSid: c.RemoteAddr().String(),
		Data: make([]byte, len(frame)), //send to another goroutine, need copy
	}
	copy(rEvent.Data, frame)

	nEvent := GetEventData()
	nEvent.NetType = EventReceive
	nEvent.Data = rEvent

	b.loop.Notify(nEvent)
	return
}

//OnClosed close has been closed
//err is the last known connection error.
func (b *gnetEventHanlderBase) OnClosed(c gnet.Conn, err error) (action gnet.Action) {
	//network clean channel Data !
	if b == nil {
		return
	}
	if b.loop == nil {
		return
	}

	if err != nil {
		slog.Error("[SBoxNetwork] %s fire error while been closed %v ! ", c.RemoteAddr().String(), err)
	}

	//已经关闭什么都不用管
	if atomic.LoadInt32(&b.status) != common.NetworkRunning {
		return
	}

	tis := c.RemoteAddr().String()

	b.rw.Lock()
	if bc, ok := b.connMap[tis]; ok {
		bc.OnClose()
		delete(b.connMap, tis)
	}
	b.rw.Unlock()

	nEvent := GetEventData()
	nEvent.NetType = EventClose
	nEvent.Data = &ClosedEvent{
		TSid: tis,
	}
	b.loop.Notify(nEvent)
	return
}

//OnShutdown server is being shut down
func (b *gnetEventHanlderBase) OnShutdown(server gnet.Server) {
	if b == nil {
		return
	}

	if b.loop == nil {
		return
	}

	nEvent := GetEventData()
	nEvent.NetType = EventShutdown
	nEvent.Data = &server
	b.loop.Notify(nEvent)
}

//Tick tick loop in goroutine
func (b *gnetEventHanlderBase) Tick() (delay time.Duration, action gnet.Action) {
	if b == nil {
		action = gnet.Close
		return
	}
	b.loop.Tick()
	return
}

// ================================= server special =================================

//OnInitComplete server is ready
func (b *serverEH) OnInitComplete(server gnet.Server) (action gnet.Action) {

	ssid := server.Addr.String()
	b.mu.Lock()
	defer b.mu.Unlock()
	if _, ok := b.servers[ssid]; ok {
		//TODO add log
		action = gnet.Close
		return
	}

	slog.Info("init server at %s successful ", server.Addr.String())

	b.servers[ssid] = server
	//check to status status
	atomic.StoreInt32(&b.status, common.NetworkRunning)
	return
}

//OnOpened new connection has been opened
//out is the return bytes to peer
func (b *serverEH) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) {
	if c == nil {
		slog.Error("[Net] opened connection handle is invalid ")
		return nil, gnet.Close
	}

	out, action = checkLoopValid(b.gnetEventHanlderBase)
	if action != gnet.None {
		slog.Error("[Net] 0,%s,0 Service Box Network moudle error !", c.RemoteAddr().String())
		return
	}
	//创建网络连接
	bc := BoxConn(c)
	tis := c.RemoteAddr().String()
	b.rw.Lock()
	if _, ok := b.connMap[tis]; ok {
		action = gnet.Close
		b.rw.Unlock()
		return
	} else {
		b.connMap[tis] = bc
	}
	b.rw.Unlock()
	//构造网络事件
	nEvent := GetEventData()
	nEvent.NetType = EventAccept
	nEvent.Data = &AcceptEvent{
		Addr: tis,
		Conn: bc,
	}
	b.loop.Notify(nEvent)
	return
}

func (ch *clientEH) OnInitComplete(gnet.Server) (action gnet.Action) {
	//check to status status
	atomic.StoreInt32(&ch.status, common.NetworkRunning)
	return
}

//func (ch *clientEH) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) {
//	out, action = checkLoopValid(ch.gnetEventHanlderBase)
//	if action != gnet.None {
//		slog.Error("[Net] 0,%s,0 Service Box Network moudle error !", c.RemoteAddr().String())
//		return
//	}
//
//	//构造网络事件
//	nEvent := GetEventData()
//	nEvent.NetType = EventRespConnect
//	nEvent.Data = &ConnectEvent{Conn: BoxConn(c)}
//	ch.loop.Notify(nEvent)
//	return
//}
